cmake_minimum_required(VERSION 3.5.0)
project(tools VERSION 0.2.7 LANGUAGES C CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)

# 设置 OpenGL 策略，避免警告
if(POLICY CMP0072)
    cmake_policy(SET CMP0072 NEW)
endif()
set(OpenGL_GL_PREFERENCE "GLVND")

# 查找依赖
find_package(yaml-cpp REQUIRED)
find_package(Boost REQUIRED COMPONENTS iostreams)
find_package(OpenCV REQUIRED)
find_package(glfw3 REQUIRED)
find_package(GLEW REQUIRED)
find_package(OpenGL REQUIRED)

file(GLOB SRC CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/*.cpp")

# 设置库目标
add_library(tools STATIC ${SRC})
target_link_libraries(tools 
    yaml-cpp 
    Boost::boost
    Boost::iostreams
    ${OpenCV_LIBS}
    glfw
    GLEW::GLEW
    OpenGL::GL
)
target_compile_options(tools PRIVATE -Wall -fPIC -fPIE)
if(OpenCV_INCLUDE_DIRS)
    target_include_directories(tools PRIVATE ${OpenCV_INCLUDE_DIRS})
endif()

target_include_directories(tools PUBLIC
	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>  # 在构建时（即构建该库时），包含${CMAKE_CURRENT_SOURCE_DIR}/include目录
	$<INSTALL_INTERFACE:include>                            # 在安装后，当其他项目使用find_package找到该库时，则包含include目录（相对于安装位置）
   )

# ============================================================================
# 版本检查和自动卸载功能（必须在所有 install 命令之前）
# ============================================================================
# 生成版本检查和卸载脚本
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckAndUninstall.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/CheckAndUninstall.cmake
    @ONLY
)

# 在 cmake_install.cmake 生成后，修改它以在开头插入卸载代码
# 创建一个自定义目标，在构建时修改 cmake_install.cmake
add_custom_target(modify_install_script ALL
    COMMAND ${CMAKE_COMMAND} -DCMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
            -DCMAKE_COMMAND=${CMAKE_COMMAND}
            -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ModifyInstallScript.cmake
    COMMENT "修改 cmake_install.cmake 以在安装前执行版本检查..."
    VERBATIM
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/CheckAndUninstall.cmake
)

# 安装库文件和头文件
install (TARGETS tools
    EXPORT toolsTargets                                     # 将该目标导出到名为toolsTargets的导出集中
    RUNTIME DESTINATION bin                                 # 将运行时文件（如DLL）安装到bin目录（相对于安装前缀）
    LIBRARY DESTINATION lib                                 # 将共享库文件安装到lib目录
    ARCHIVE DESTINATION lib                                 # 将静态库或归档文件安装到lib目录   
    INCLUDES DESTINATION include                            # 将目标的公共头文件安装到include目录
)

install (DIRECTORY include/ DESTINATION include)            # 将include/目录下的所有文件安装到目标系统的include目录下

# 生成并安装配置文件
include(CMakePackageConfigHelpers)                          # 包含一个CMake辅助模块，用于生成包配置文件

# 生成配置版本文件
# 生成一个基本的版本配置文件toolsConfigVersion.cmake。它定义了包版本和兼容性（AnyNewerVersion表示只要主版本相同，更新版本都可以接受）。
write_basic_package_version_file(
	toolsConfigVersion.cmake
	VERSION ${PROJECT_VERSION}
	COMPATIBILITY AnyNewerVersion
)

# 生成项目配置文件
# 根据模板文件cmake/toolsConfig.cmake.in生成包配置文件toolsConfig.cmake，并放在构建目录下。
# 同时指定了安装位置为lib/cmake/tools，该路径会被写入到生成的配置文件中，以便正确找到目标文件。
configure_package_config_file(
	${CMAKE_CURRENT_SOURCE_DIR}/cmake/toolsConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/toolsConfig.cmake
    INSTALL_DESTINATION lib/cmake/tools
)

# 安装配置文件
# 将生成的toolsConfig.cmake和toolsConfigVersion.cmake文件安装到lib/cmake/tools目录。
install (FILES
    ${CMAKE_CURRENT_BINARY_DIR}/toolsConfig.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/toolsConfigVersion.cmake
    DESTINATION lib/cmake/tools
)

# 安装导出文件（包含目标信息）
install (EXPORT toolsTargets
    FILE toolsTargets.cmake
    NAMESPACE tools::
    DESTINATION lib/cmake/tools)

# ============================================================================
# 安装后处理：保存 install_manifest.txt
# ============================================================================
# 在安装后保存 install_manifest.txt 到安装目录，以便后续卸载使用
# 注意：需要先生成 SaveManifest.cmake 脚本
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/cmake/SaveManifest.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/SaveManifest.cmake
    @ONLY
)
install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/SaveManifest.cmake)

# ============================================================================
# 测试配置
# ============================================================================
enable_testing()

# 测试目录
set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests)

# 测试可执行文件列表
set(TEST_EXECUTABLES
    test_bash
    test_file
    test_log
    test_timer
    test_param
    test_image
)

# 为每个测试创建可执行文件
foreach(TEST_NAME ${TEST_EXECUTABLES})
    add_executable(${TEST_NAME} ${TEST_DIR}/${TEST_NAME}.cpp)
    target_link_libraries(${TEST_NAME} 
        tools
        yaml-cpp
        Boost::boost
        Boost::iostreams
        ${OpenCV_LIBS}
        glfw
        GLEW::GLEW
        OpenGL::GL
    )
    target_include_directories(${TEST_NAME} PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
        ${TEST_DIR}
    )
    if(OpenCV_INCLUDE_DIRS)
        target_include_directories(${TEST_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS})
    endif()
    target_compile_options(${TEST_NAME} PRIVATE -Wall)
    
    # 添加测试
    add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
endforeach()

# 创建运行所有测试的可执行文件
add_executable(test_all ${TEST_DIR}/test_all.cpp)
target_link_libraries(test_all 
    tools
    yaml-cpp
    Boost::boost
    Boost::iostreams
    ${OpenCV_LIBS}
    glfw
    GLEW::GLEW
    OpenGL::GL
)
target_include_directories(test_all PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/include
    ${TEST_DIR}
)
if(OpenCV_INCLUDE_DIRS)
    target_include_directories(test_all PRIVATE ${OpenCV_INCLUDE_DIRS})
endif()
target_compile_options(test_all PRIVATE -Wall)

# 添加运行所有测试的测试
add_test(NAME test_all COMMAND test_all)

# 设置测试属性：在编译后自动运行测试
# 创建一个自定义目标，在编译后运行测试
add_custom_target(run_tests
    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
    DEPENDS ${TEST_EXECUTABLES} test_all
    COMMENT "Running all tests..."
)

# 创建一个自定义目标，在编译后运行所有测试
add_custom_target(run_all_tests
    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
    DEPENDS ${TEST_EXECUTABLES} test_all tools
    COMMENT "Running all tests..."
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# 设置ALL目标，使测试在默认构建时运行
# 创建一个测试运行目标，依赖于所有测试可执行文件
add_custom_target(check
    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
    DEPENDS ${TEST_EXECUTABLES} test_all
    COMMENT "Running tests..."
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# 将check目标添加到ALL，这样每次make都会运行测试
# 注意：这会在编译后自动运行测试
set_target_properties(check PROPERTIES EXCLUDE_FROM_ALL FALSE)

# 确保tools库在测试之前构建完成
foreach(TEST_NAME ${TEST_EXECUTABLES})
    add_dependencies(${TEST_NAME} tools)
endforeach()
add_dependencies(test_all tools)
add_dependencies(check tools)
